﻿#include "stdafx.h"
#include "sbcs.h"
#include "extend/sbmem.h"
#include "kstub_mutex.h"

#ifndef SB_NO_MEM

#define USE_MPF_LOCK		1

#if USE_MPF_LOCK
// 더 빠르지만 잠김 우선 순위 기준이 없다
#define MPF_LOCK			K_LOCK(k_impl_mpf.lock)
#define MPF_UNLOCK			K_UNLOCK(k_impl_mpf.lock)
#else
// 조금 느리지만 잠김 순서 대로 처리된다
#define MPF_LOCK			kp_lock()
#define MPF_UNLOCK			kp_unlock()
#endif


//////////////////////////////////////////////////////////////////////////
// 프루프 메모리
static struct kImplMemProof
{
	kMemTable			tbl;

	kMemBlock*			frst;
	kMemBlock*			last;
	ksize_t				index;
	ksize_t				count;

#if USE_MPF_LOCK
	kLock				lock;
#endif

	kint				dotype;     // debug output type [0=normal, 1=hex, 2=text]
} k_impl_mpf =
{
	{
		k_mpffree,
		k_mpfalloc,
		k_mpfallocf,
	},
	NULL, NULL, 0, 0,
#if USE_MPF_LOCK
	KLOCK_INIT,
#endif
	0
};

// 초기화
void _k_memproof_init(void)
{
	k_memtable(&k_impl_mpf.tbl);
}

// 제거
void _k_memproof_disp(void)
{
	if (k_impl_mpf.count == 0 && k_impl_mpf.frst == NULL && k_impl_mpf.last == NULL)
		k_trace(KLV_RESULT, "k.mpf", "safe.");
	else
	{
		kMemBlock* node;
		kMemBlock* next;
		ksize_t sums, sumb;
		double size;
		int usage;
#if _SB_WINDOWS_
		int isdbg = k_candebug();
		ksize_t idx = 1;
#endif
		char dump[64];

		k_tracef(KLV_CRITICAL, "k.mpf", "found %d proof memories.", k_impl_mpf.count);

		for (sumb = sums = 0, node = k_impl_mpf.frst; node; node = next)
		{
			if (node->line)
			{
#if !_SB_WINDOWS_
				k_tracef(KLV_INFO, "k.mpf", "size=%Lu(%Lu), \"%s : %d\"",
					node->size, node->block, node->filename, node->line);
#else
				if (isdbg)
				{
					k_mesgf("%s(%d) : {%Lu}", node->filename, node->line, idx++);

					if (k_impl_mpf.dotype == 1)
					{
						// 헥사
						k_mesgf(" size=%Lu(%Lu) [%s]\n", node->size, node->block,
							k_memdmp_hex(k_memptr(node), node->size, dump, 42));
					}
					else if (k_impl_mpf.dotype == 2)
					{
						// 텍스트
						k_mesgf(" size=%Lu(%Lu) <%s>\n", node->size, node->block,
							k_memdmp(k_memptr(node), node->size, dump, 42));
					}
					else
					{
						// 자동
						if (node->size > 16)
						{
							k_mesgf(" size=%Lu(%Lu) <%s>\n", node->size, node->block,
								k_memdmp(k_memptr(node), node->size, dump, 42));
						}
						else
						{
							k_mesgf(" size=%Lu(%Lu) [%s]\n", node->size, node->block,
								k_memdmp_hex(k_memptr(node), node->size, dump, 42));
						}
					}
				}
				else
				{
					k_tracef(KLV_INFO, "k.mpf", "size=%Lu(%Lu), \"%s : %d\"",
						node->size, node->block, node->filename, node->line);
				}
#endif
			}
			else
			{
				if (node->size > 16)
				{
					k_tracef(KLV_INFO, "k.mpf", "size=%Lu(%Lu), <%s>",
						node->size, node->block, k_memdmp(k_memptr(node), node->size, dump, 42));
				}
				else
				{
					k_tracef(KLV_INFO, "k.mpf", "size=%Lu(%Lu), [%s]",
						node->size, node->block, k_memdmp_hex(k_memptr(node), node->size, dump, 42));
				}
			}

			next = node->next;
			sums += node->size;
			sumb += node->block;
			k_heapfree(node);
		}

		k_heapdec(sumb);

		usage = k_memudch(sums, &size);

		if (usage == ' ')
			k_tracef(KLV_INFO, "k.mpf", "user size : %Lu bytes", sums);
		else
			k_tracef(KLV_INFO, "k.mpf", "user size : %f %cbytes", size, usage);

		usage = k_memudch(sumb, &size);

		if (usage == ' ')
			k_tracef(KLV_INFO, "k.mpf", "block size : %Lu bytes", sumb);
		else
			k_tracef(KLV_INFO, "k.mpf", "block size : %f %cbytes", size, usage);
	}
}

// 블럭 추가
static void _k_memproof_add(kMemBlock* node, ksize_t block)
{
	MPF_LOCK;

	if (k_impl_mpf.frst)
		k_impl_mpf.frst->prev = node;
	else
		k_impl_mpf.last = node;

	node->next = k_impl_mpf.frst;
	node->prev = NULL;

	k_impl_mpf.frst = node;

	k_impl_mpf.count++;
	k_impl_mpf.index++;
	k_heapinc(block);

	MPF_UNLOCK;
}

// 블럭 제거
static void _k_memproof_remove(kMemBlock* node, ksize_t block)
{
	MPF_LOCK;

	if (node->next)
		node->next->prev = node->prev;
	else
	{
		k_assert_eq(k_impl_mpf.last, node);
		k_impl_mpf.last = node->prev;
	}

	if (node->prev)
		node->prev->next = node->next;
	else
	{
		k_assert_eq(k_impl_mpf.frst, node);
		k_impl_mpf.frst = node->next;
	}

	k_impl_mpf.count--;
	k_heapdec(block);

	MPF_UNLOCK;
}

//////////////////////////////////////////////////////////////////////////
// 프루프

// 크기
ksize_t k_mpfsize(void)
{
	return k_heapsize();
}

// 개수
ksize_t k_mpfcount(void)
{
	return k_impl_mpf.count;
}

// 할당
kpointer k_mpfalloc(ksize_t block)
{
	kMemBlock* node;

	k_assert_0(block > 0);
	node = (kMemBlock*)k_heapalloc(block);

	if (!node)
	{
		k_tracef(KLV_FATAL, "k.mpf", "cannot allocate memory! (block size: %Lu)", block);
		return NULL;
	}

	_k_memproof_add(node, block);

	node->tbl = &k_impl_mpf.tbl;
	node->type = 1;
	node->index = 0;
	node->req = 0;
	node->block = block;

#if _SB_DEBUG_
#if !_SB_64_
	node->align0 = 11223344;
	node->align1 = 44332211;
	node->alidx = k_impl_mpf.index;
#endif
	node->gap = KMEM_DBG;
#endif

	return k_memptr(node);
}

// 할당
kpointer k_mpfallocf(ksize_t block)
{
	kMemBlock* node;

	k_assert_0(block > 0);
	node = (kMemBlock*)k_heapallocf(block);

	if (!node)
	{
		k_tracef(KLV_FATAL, "k.mpf", "cannot allocate memory! (block size: %Lu)", block);
		return NULL;
	}

	_k_memproof_add(node, block);

	node->tbl = &k_impl_mpf.tbl;
	node->type = 1;
	node->index = 0;
	node->req = 0;
	node->block = block;

#if _SB_DEBUG_
#if !_SB_64_
	node->align0 = 11223344;
	node->align1 = 44332211;
	node->alidx = k_impl_mpf.index;
#endif
	node->gap = KMEM_DBG;
#endif

	return k_memptr(node);
}

// 해제
void k_mpffree(kpointer ptr)
{
	kMemBlock* node;

	k_assert_0(ptr != NULL);

	node = k_memhdr(ptr);

#if _SB_DEBUG_
	if (!k_heapisvalid(node))
		k_halt("k.heap", "TRY TO FREE THE FREED MEMORY?");
#endif

#if _SB_DEBUG_
#if !_SB_64_
	node->align0 = 10101010;
	node->align1 = 90909090;
#endif
	node->gap = KMEM_END;
#endif

	_k_memproof_remove(node, node->block);
	k_heapfree(node);
}

// 디버그 출력 타입
void k_mpfdotn(kint type)
{
	k_impl_mpf.dotype = type;
}

// 목록
void k_mpfprint(kcham byhex)
{
	kMemBlock* node;
	ksize_t ss, sb, cnt;   // ss=크기합, sb=블럭합
	double size;
	int usage;
	char dmp[64];

	k_mesgf(" %-04s | %-8s | %-8s | %-s\n", "no", "size", "block", "desc");

	MPF_LOCK;

	for (sb = ss = 0, cnt = 1, node = k_impl_mpf.frst; node; node = node->next, cnt++)
	{
		if (node->line)
		{
			k_mesgf(" %-4d | %-8Lu | % -8Lu | \"%s:%d\"\n",
				cnt, node->size, node->block, node->filename, node->line);
		}
		else
		{
			if (byhex)
			{
				k_mesgf(" %-4d | %-8Lu | % -8Lu | <%s>\n",
					cnt, node->size, node->block, k_memdmp_hex(k_memptr(node), node->size, dmp, 42));
			}
			else
			{
				k_mesgf(" %-4d | %-8Lu | % -8Lu | [%s]\n",
					cnt, node->size, node->block, k_memdmp(k_memptr(node), node->size, dmp, 42));
			}
		}

		ss += node->size;
		sb += node->block;
	}

	MPF_UNLOCK;

	usage = k_memudch(ss, &size);
	k_mesgf("--- user size : %.3g%cbytes ---\n", size, usage);

	usage = k_memudch(sb, &size);
	k_mesgf("--- block size : %.3g%cbytes ---\n", size, usage);

	k_mesgf("--- total proofs : %Lu ---\n", cnt - 1);
}

#endif  // SB_NO_MEM
